from tkinter import *  # 导入 tkinter 库中的所有公共对象

# 定义创建框架的函数，简化框架创建过程
def 创建框架(父组件):
    """将共同的属性作为默认值, 以简化Frame创建过程"""
    框架实例 = Frame(父组件)  # 创建一个框架实例，父组件为传入的参数
    框架实例.pack(side=TOP, expand=YES, fill=BOTH)  # 设置框架的布局，使其在顶部，可扩展并填充可用空间
    return 框架实例  # 返回创建的框架实例

# 定义创建按钮的函数，简化按钮创建过程
def 创建按钮(父组件, 显示文本, 命令函数):
    """提取共同的属性作为默认值, 使Button创建过程简化"""
    按钮实例 = Button(父组件, text=显示文本, command=命令函数, width=6)  # 创建一个按钮实例，设置文本、命令和宽度
    按钮实例.pack(side=LEFT, expand=YES, fill=BOTH, padx=2, pady=2)  # 设置按钮的布局，使其在左侧，可扩展并填充，并设置内外边距
    return 按钮实例  # 返回创建的按钮实例

# 定义计算表达式的函数
def 计算表达式(表达式字符串):
    """用eval方法计算表达式字符串"""
    try:  # 尝试执行以下代码
        if (千位分隔符标志.get() == 0):  # 如果千位分隔符标志为0（即不使用分隔符）
            return eval(删除千位分隔符(表达式字符串))  # 计算删除分隔符后的表达式字符串
        else:  # 否则（即使用千位分隔符）
            return 添加千位分隔符(str(eval(删除千位分隔符(表达式字符串))))  # 计算表达式后，将结果转换为字符串并添加千位分隔符
    except (SyntaxError, ZeroDivisionError, NameError):  # 如果发生语法错误、除零错误或名称错误
        return '错误'  # 返回错误提示

# 定义退格函数
def 退格(表达式字符串):
    """将表达式字符串最末的字符删除并返回"""
    if len(表达式字符串) > 0:  # 如果表达式字符串长度大于0
        return 表达式字符串[:-1]  # 返回去掉最后一个字符的字符串
    else:  # 否则
        return 表达式字符串  # 返回原字符串

# 定义添加千位分隔符的函数
def 添加千位分隔符(数字字符串):
    """向参数传入的数字串中添加千位分隔符

    这里考虑了三种情况: 无整数部份, 无小数部份, 同时有整数和小数部份
    由于字符串是不可改变的, 这里由字符串生成列表以便执行insert操作和
    extend操作, 操作完成后最由列表生成字符串返回
    """
    小数点位置 = 数字字符串.find('.')  # 查找小数点的位置
    if 小数点位置 > 0:  # 如果小数点存在且不在第一位
        整数部分 = 数字字符串[:小数点位置]  # 获取整数部分
        小数部分 = 数字字符串[小数点位置:]  # 获取小数部分
    elif 小数点位置 < 0:  # 如果没有小数点
        整数部分 = 数字字符串  # 整个字符串都是整数部分
        小数部分 = ''  # 小数部分为空
    else:  # 如果小数点在第一位 (例如 .5)
        整数部分 = ''  # 整数部分为空
        小数部分 = 数字字符串  # 整个字符串都是小数部分

    字符列表 = [字符 for 字符 in 整数部分]  # 将整数部分转换为字符列表
    列表长度 = len(字符列表)  # 获取字符列表的长度
    临时索引 = 3  # 初始化临时索引，用于定位插入逗号的位置
    while 列表长度 - 临时索引 > 0:  # 当剩余长度大于0时，循环插入逗号
        字符列表.insert(列表长度 - 临时索引, ',')  # 在指定位置插入逗号
        临时索引 += 3  # 更新临时索引
    字符列表.extend(小数部分)  # 将小数部分追加到字符列表末尾
    新字符串 = ''  # 初始化新字符串
    for 字符 in 字符列表:  # 遍历字符列表
        新字符串 += 字符  # 将每个字符连接起来形成新字符串

    return 新字符串  # 返回添加了千位分隔符的新字符串

# 定义删除千位分隔符的函数
def 删除千位分隔符(数字字符串):
    """删除数字串中所有的千位分隔符"""
    return 数字字符串.replace(',', '')  # 将字符串中的逗号替换为空字符串


# --- GUI界面实现 --- #
主窗口 = Tk()  # 创建 Tkinter 主窗口实例
主窗口.resizable(width=False, height=False)  # 设置窗口大小不可调整

主窗口.title("计算器")  # 设置窗口标题为“计算器”

主菜单栏 = Menu(主窗口)  # 创建主菜单栏，并将其附加到主窗口

# 创建“菜单”下拉菜单
文件菜单 = Menu(主菜单栏, tearoff=0)  # 创建一个名为“文件菜单”的菜单项，tearoff=0表示菜单不能被拖出
文件菜单.add_command(label='退出', command=lambda: exit())  # 向文件菜单添加“退出”命令，点击时执行退出程序的操作
主菜单栏.add_cascade(label='菜单', menu=文件菜单)  # 将“文件菜单”作为级联菜单添加到主菜单栏，标签为“菜单”

显示内容 = StringVar()  # 创建一个 StringVar 对象，用于在文本框中显示和更新内容
千位分隔符标志 = IntVar()  # 创建一个 IntVar 对象，用于标记是否使用千位分隔符
千位分隔符标志.set(0)  # 初始化千位分隔符标志为0，表示默认不使用
# view_menu = Menu(main_menus, tearoff=0) # 这行代码似乎未被使用，暂时注释掉

主窗口['menu'] = 主菜单栏  # 将创建的主菜单栏设置为主窗口的菜单

# 创建文本输入框
输入框 = Entry(主窗口, textvariable=显示内容, font=("Arial", 16), justify=RIGHT) # 创建一个文本输入框，关联显示内容变量，设置字体和右对齐
输入框.pack(expand=YES, fill=BOTH, padx=5, pady=5)  # 设置输入框的布局，使其可扩展并填充，并设置内外边距

# --- 创建按钮行 --- #

# 创建第一行按钮（回退、清空）
第一行按钮框架 = 创建框架(主窗口)  # 为第一行按钮创建一个框架
创建按钮(第一行按钮框架, '←', lambda: 显示内容.set(退格(显示内容.get())))  # 创建“回退”按钮
创建按钮(第一行按钮框架, 'C', lambda: 显示内容.set(''))  # 创建“清空”按钮

# 定义按钮布局字符串，用于批量创建按钮
按钮布局 = (
    '789/',
    '456*',
    '123-',
    '0.='
)

# 循环创建数字和运算符按钮
for 行按钮文本 in 按钮布局:  # 遍历按钮布局中的每一行
    当前行框架 = 创建框架(主窗口)  # 为当前行按钮创建一个框架
    for 单个按钮文本 in 行按钮文本:  # 遍历当前行中的每个按钮字符
        if 单个按钮文本 == '=':  # 如果按钮是等号
            创建按钮(当前行框架, 单个按钮文本, 
                     lambda 当前显示=显示内容: 当前显示.set(计算表达式(当前显示.get())))  # 创建等号按钮，点击时计算表达式
        else:  # 如果按钮是数字或其他运算符
            创建按钮(当前行框架, 单个按钮文本, 
                     lambda 当前显示=显示内容, 字符=单个按钮文本: 当前显示.set(当前显示.get() + 字符)) # 创建按钮，点击时将字符追加到显示内容

if __name__ == '__main__':  # 如果当前脚本是主程序
    主窗口.mainloop()  # 进入 Tkinter 事件循环，等待用户操作
